package top.codedance.iotp.client.plugins.async.com;

import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.client.ClientApplication;
import top.codedance.iotp.common.executer.AsyncAbstractExecuter;
import top.codedance.iotp.common.entity.CommonEntity;
import top.codedance.iotp.common.util.BytesBuilder;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import org.apache.log4j.Logger;

import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;

public class ComService extends Service {
    private Logger logger = ClientApplication.getLogger(ComService.class);
    private SerialPort mSerialPort;
    private Integer instance;

    public ComService(AsyncAbstractExecuter executer, ComServerParam param, Integer instance) {
        super(executer, param);
        this.instance = instance;
    }

    public synchronized void createConnect() throws Exception {
        mSerialPort = SerialPort.getCommPort(param.getComName());
        mSerialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
        mSerialPort.setComPortParameters(param.getBaudRate(), param.getDataBits(), param.getStopBits(), param.getParity());
        mSerialPort.addDataListener(new SerialPortDataListener(){
            private BytesBuilder gbb = new BytesBuilder();

            @Override
            public int getListeningEvents() {
                return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
            }

            @Override
            public synchronized void serialEvent(SerialPortEvent serialPortEvent) {
                if (serialPortEvent.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
                    return;
                BytesBuilder bb = new BytesBuilder();
                SerialPort serialPort = serialPortEvent.getSerialPort();
                int avai = serialPort.bytesAvailable();
                while (avai > 0) {
                    byte[] data = new byte[avai];
                    serialPort.readBytes(data, avai);
                    bb.append(data);
                    avai = serialPort.bytesAvailable();
                }
                byte[] bytes = bb.toArray();
                gbb.append(bytes);
                receipt(DatatypeConverter.printHexBinary(gbb.toArray()));
                gbb = new BytesBuilder();
            }
        });
        if (!mSerialPort.openPort(0, 1024*1024*2, 1024*1024*2)) {
            mSerialPort = null;
        }
        logger.debug("Async com client is running!");
    }

    public void close(){
        if(mSerialPort != null && mSerialPort.isOpen()) {
            mSerialPort.closePort();
        }
    }

    @Override
    public void receipt(String body) {
        synchronized (ComExecuterImpl.class) {
            Set<Map.Entry<Integer, Set<String>>> entries = ComExecuterImpl.tasks.entrySet();
            for (Map.Entry<Integer, Set<String>> entry : entries){
                if(entry.getKey() == instance){
                    CommonEntity ce = genCommonEntity();
                    ce.setBody(body.getBytes(StandardCharsets.UTF_8));
                    Set<String> value = entry.getValue();
                    for (String cid : value){
                        executer.send(ce.build(), cid);
                        logger.debug("Send data to " + cid);
                    }
                    break;
                }
            }
        }
    }

    @Override
    public void send(String command) {
        try {
            if (mSerialPort != null && mSerialPort.isOpen()) {
                byte[] bytes = command.getBytes();
                mSerialPort.writeBytes(bytes, bytes.length);
            }
        }catch (Exception e){
            ComExecuterImpl.coms.remove(instance);
        }
    }

    @Override
    public void send(byte[] bytes) {
        try {
            if (mSerialPort != null && mSerialPort.isOpen()) {
                mSerialPort.writeBytes(bytes, bytes.length);
            }
        }catch (Exception e){
            ComExecuterImpl.coms.remove(instance);
        }
    }
}
